/*
  Program: Mine Mayhem
  File:    fieldhex.cc
  Date:    26 October to 3 December,	      version 2.00
	   8 December to 4 January,  2001,    version 2.10
	   15 January,			      version 2.11
	   18 September to 16 December, 2006, version 2.90

  Everything to do with the hexagonal minefield.

  A lot of this code is based on Coyote's Hexagonal Minesweeper.  I really
  thank him for providing me with his source.

  Freeware, Copyright 2006 Jason Hood.


  The minefield looks like:	    x x x
				   x x x x
				  x x x x x
				   x x x x
				    x x x

  which is stored as:		  x x x . .
				  x x x x .
				  x x x x x    -
				  . x x x x    | height
				  . . x x x    -

				      |---| width
*/


#include "minefld.h"
#include "options.h"
#include "tiles.h"
#include "board.h"


HexagonField::HexagonField() : Minefield()
{
  Beginner     = Layout( 5,  5, 10);
  Intermediate = Layout(10, 10, 45);
  Expert       = Layout(13, 13, 95);

  MinWidth  = MinHeight = 5;
  max_cols  = max_width / HEXAGONW;
  max_rows  = (max_height - HEXAGOND) / HEXAGONR;
  MaxHeight = (max_rows + 1) / 2;

  CreateMinefield();
}


/* -----------------------------   EveryTile   ------------------------------

  Create the list of hexagons on the minefield.

*/

void HexagonField::EveryTile()
{
  int p, x, y;
  int x1, x2, xc;

  cols = layout.dim.x + layout.dim.y - 1;
  rows = layout.dim.y * 2 - 1;

  p = tile_count = 0;
  for (y = 0; y < rows; ++y)
  {
    if (y < layout.dim.y)
    {
      x1 = 0;
      x2 = y + layout.dim.x - 1;
    }
    else
    {
      x1 = y - (layout.dim.y - 1);
      x2 = cols - 1;
    }
    xc = x1 * HEXAGONW + (layout.dim.y - 1 - y) * HEXAGONW/2;

    for (x = 0; x < x1; ++x)
      grid[y][x] = NULL;

    for (; x <= x2; xc += HEXAGONW, ++x)
    {
      field[tile_count].grid.x = x;
      field[tile_count].grid.y = y;
      field[tile_count].loc.x  = xc;
      field[tile_count].loc.y  = y * HEXAGONR;

      grid[y][x] = &field[tile_count];

      if (y == 0 || y == rows-1 || x == x1 || x == x2)
	perimeter[p++] = tile_count;

      ++tile_count;
    }

    for (; x < cols; ++x)
      grid[y][x] = NULL;
  }
  perimeter[p] = -1;

  foralltiles
    Surround(&field[tile]);

  pan_wid = (cols - 1) * HEXAGONW;
}


/* ------------------------------   Surround   ------------------------------

  Determine the surrounding hexagons, where the order is:

     6 4   [ 6 4   ]
    5 0 2  [ 5 0 2 ]
     3 1   [   3 1 ]

  This is to reduce opening hexagons in the cross game.

*/

void HexagonField::Surround(Tile* t)
{
  static cCoord ofs[] = { Coord( 0, 0), Coord(+1,+1),
			  Coord(+1, 0), Coord( 0,+1), Coord( 0,-1),
					Coord(-1, 0), Coord(-1,-1) };

  Minefield::Surround(t, 7, ofs);
}


/* --------------------------------   Area   --------------------------------

  Area of a "hexagon" - two trapezoids plus the middle line (simplified).

*/

int HexagonField::area(cCoord& dim) const
{
  int w = dim.x - 1;
  return ((w + w + dim.y) * dim.y - w);
}


/* ----------------------------   GetMaxWidth	-----------------------------

  The maximum "width" of the hexagon is dependant on its height.  (The
  _overall_ maximum width doesn't change (cf. max_cols), but that's not the
  dimension I'm using.)

*/

int HexagonField::GetMaxWidth(int height)
{
  return (MaxWidth = max_cols - height + 1);
}


/* -----------------------------   RandomDim   ------------------------------

  Generate a random dimension.

*/

void HexagonField::RandomDim()
{
  Random.dim.y = random() % (MaxHeight - MinHeight + 1) + MinHeight;
  Random.dim.x = random() % (GetMaxWidth(Random.dim.y) - MinWidth + 1)
		 + MinWidth;
}


/* -------------------------   GetBoardDimensions   -------------------------

  Given the dimensions of the minefield, return the dimensions of the board.

*/

Coord HexagonField::GetBoardDimensions(cCoord& dim)
{
  return Coord((dim.x + dim.y - 1) * HEXAGONW,
	       (dim.y * 2 - 1) * HEXAGONR + HEXAGOND);
}


/* ------------------------------   Outline   -------------------------------

  Draw the outline around the minefield.

*/

void HexagonField::Outline(ViewBuffer* buf, int x1, int y1, int x2, int y2)
{
  int dx = (layout.dim.y - 1) * (HEXAGONW/2);
  int lx = x1+dx, rx = x2-dx - HEXAGONW/2,
      ty = y1,	  by = y2 - HEXAGONR;

  for (int y = 0; y < layout.dim.y; ++y)
  {
    // Top-left
    BitBltTransparent(buf, lx, ty, Hexagon[HEXOUTLINE1],
		      HEXAGONW/2-1, 0, HEXAGONW-1, HEXAGONR);
    // Top-right
    BitBltTransparent(buf, rx, ty, Hexagon[HEXOUTLINE1],
		      0, 0, HEXAGONW/2, HEXAGONR);
    // Bottom-left
    BitBltTransparent(buf, lx, by, Hexagon[HEXOUTLINE2],
		      HEXAGONW/2-1, 0, HEXAGONW-1, HEXAGONR);
    // Bottom-right
    BitBltTransparent(buf, rx, by, Hexagon[HEXOUTLINE2],
		      0, 0, HEXAGONW/2, HEXAGONR);

    lx -= HEXAGONW/2;
    rx += HEXAGONW/2;
    ty += HEXAGONR;
    by -= HEXAGONR;
  }

  lx = x1+1+dx + HEXAGONW/2;
  ty = y1;
  by = y2 - HEXAGOND;
  for (int x = 1; x < layout.dim.x; ++x)
  {
    // Top
    BitBltTransparent(buf, lx, ty, Hexagon[HEXOUTLINE1],
		      0, 0, HEXAGONW-1, HEXAGOND);
    // Bottom
    BitBltTransparent(buf, lx, by, Hexagon[HEXOUTLINE2],
		      0, HEXAGONR-HEXAGOND, HEXAGONW-1, HEXAGONR);
    lx += HEXAGONW;
  }
}


/* ------------------------------   Convert   -------------------------------

  Convert the mouse co-ordinate to a grid co-ordinate.

*/

void HexagonField::Convert(int& x, int& y)
{
  int c, r;

  r  = y % HEXAGONR;
  y /= HEXAGONR;

  x -= (HEXAGONW/2) * (layout.dim.y - 1 - y);
  if (x < 0)
  {
    if (x <= -HEXAGONW)
      return;
    c = x + HEXAGONW;
    x = -1;
  }
  else
  {
    c  = x % HEXAGONW;
    x /= HEXAGONW;
  }

  if (r < HEXAGOND)
  {
    r = HexMask[r][c];
    if (r)
    {
      --y;
      if (r == 1)
	--x;
    }
  }
}


/* ----------------------------   DisplayTile	-----------------------------

  Display the hexagon t using which bitmap.

*/

void HexagonField::DisplayTile(Tile* t, int which)
{
  Minefield::DisplayTile(t, which, HEXAGONW-1, HEXAGONH-1, Hexagon);
}


/* ------------------------------   Validate   ------------------------------

  Ensure the layout does not exceed the limits.

*/

void HexagonField::Validate(Layout& layout)
{
  if (layout.dim.y < MinHeight)
    layout.dim.y = MinHeight;
  else if (layout.dim.y > MaxHeight)
    layout.dim.y = MaxHeight;

  if (layout.dim.x < MinWidth)
    layout.dim.x = MinWidth;
  else
  {
    int max = GetMaxWidth(layout.dim.y);
    if (layout.dim.x > max)
      layout.dim.x = max;
  }

  Minefield::Validate(layout);
}


/* -------------------------   ReadIni & WriteIni   -------------------------

  As you can see, it uses the "[ Hexagonal ]" section and times are kept in
  the "minehex.tym" file.

*/

void HexagonField::ReadIni(IniFile& ini)
{
  Minefield::ReadIni(ini, "Hexagonal", "minehex.tym");
}

void HexagonField::WriteIni(IniFile& ini)
{
  Minefield::WriteIni(ini, "Hexagonal", "minehex.tym");
}
